package emulator.shell.GUI.debugger;

import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;

import javax.swing.JPanel;
import javax.swing.JScrollBar;

import sun.reflect.generics.reflectiveObjects.NotImplementedException;

@SuppressWarnings("serial")
public class ScrollView extends JPanel {

    private Font font = new Font("Monospaced", Font.PLAIN, 12);
	private int border = 3;
	private LineMetrics line_metrics = new LineMetrics();
	private JScrollBar scroll_bar;
	ScrollPane scroll_pane = null;
	
	/* to implement ! */
	boolean isPanelActive() {
		throw new NotImplementedException();
	}

	void setCurrentPosition(int desired_position) {
		throw new NotImplementedException();
	}

	int getCurrentPosition() {
		throw new NotImplementedException();
	}

	void setCurrentRange(int current_range) {
		throw new NotImplementedException();
	}

	int getCurrentRange() {
		throw new NotImplementedException();
	}

	void setVisibleLines(int i) {
		throw new NotImplementedException();
	}

	int getVisibleLines() {
		throw new NotImplementedException();
	}
	
	/* getters/setters */
	public Font getTextFont() {
		return font;
	}
	
	public int getBorderSize() {
		return border;
	}
	
	public LineMetrics getLineMetrics() {
		return line_metrics;
	}
	
	public ScrollPane getScrollPane() {
		return scroll_pane;
	}

	/* implementation */

	public void initScrollBar()
	{
	    scroll_bar = new JScrollBar(JScrollBar.VERTICAL);
	    scroll_bar.addAdjustmentListener(new ScrollBarListener());
		scroll_bar.setValues(getCurrentPosition(), getVisibleLines(), 0, getCurrentRange());
		scroll_bar.setEnabled(false);
	    add(scroll_bar, BorderLayout.EAST);
	}

	public void showScrollBar(boolean show) {
		scroll_bar.setEnabled(show);
	}

	public void ensureVisible(int index, boolean update) {
		if (!isVisibleLine(index))
			moveTo(index - getVisibleLines()/2, update);
	}

	boolean isVisibleLine(int index) {
		return getCurrentPosition() <= index && index < getCurrentPosition() + getVisibleLines();
	}

	private void adjustScrollBar() {
		scroll_bar.setValues(getCurrentPosition(), getVisibleLines(), 0, getCurrentRange());
        scroll_bar.setValueIsAdjusting(true);
	}
	
	private void moveTo(int desired_position, boolean update)
	{
		if (isPanelActive())
		{
			setCurrentPosition(desired_position);
			if (update)
				repaint();
		}
	}

	public int hitTest(Point point) {
		if (isPanelActive())
		{
			int line_rel = (point.y - getBorderSize()) / getLineMetrics().getLineHeight();
			if (line_rel >= 0 && line_rel < getVisibleLines())
				return getCurrentPosition() + line_rel;
		}
		return -1;
	}

	/* (non-Javadoc)
	 * @see javax.swing.JComponent#paint(java.awt.Graphics)
	 */
	@Override
	public void paint(Graphics g) {
		if (isPanelActive())
		{
	        getScrollPane().calcVisibleLines();
	        adjustScrollBar();
		}
		super.paint(g);
	}

	public class ScrollPane extends JPanel {

		protected void printString(Graphics g,String s,int x,int y)
	    {
	        printString(g, s, x, y, new Rectangle());
	    }

		protected void printString(Graphics g, String s, int x, int y,
				Rectangle bounding_rect) {
			getBoundingRect(s, x, y, bounding_rect);
			g.drawString(s, bounding_rect.x, bounding_rect.y);
		}

		protected void getBoundingRect(String s, int x, int y,
				Rectangle bounding_rect) {
			FontMetrics fn=getFontMetrics(getTextFont());
	        bounding_rect.x = ((fn.stringWidth("M")+1)*x) + border;
	        bounding_rect.height = fn.getHeight();
			bounding_rect.y = ((bounding_rect.height*(y+1))-fn.getMaxDescent()) + border;
			bounding_rect.width = fn.stringWidth(s);
		}
		
		protected int getTextWidth(int dx)
		{
	        FontMetrics fn=getFontMetrics(getTextFont());
	        return ((fn.stringWidth("M")+1)*dx);
		}

		public void calcVisibleLines() {
			FontMetrics fn = getFontMetrics(getTextFont());
	        Rectangle rec = getBounds();
	        line_metrics.setLineWidth(rec.width - 2*border);
	        line_metrics.setLineHeight(fn.getHeight());
			setVisibleLines(((rec.height - 2*border) / line_metrics.getLineHeight()));
		}

		public int hitTest(Point point) {
			if (isPanelActive())
			{
				int line_rel = (point.y - border) / line_metrics.getLineHeight();
				if (line_rel >= 0 && line_rel < getVisibleLines())
					return getCurrentPosition() + line_rel;
			}
			return -1;
		}
		
	}

	class ScrollBarListener implements AdjustmentListener
	{
		@Override
		public void adjustmentValueChanged(AdjustmentEvent event) {
			moveTo(event.getValue(), true);
		}
	}

	class MouseScrollListener implements MouseWheelListener
	{

		@Override
		public void mouseWheelMoved(MouseWheelEvent event) {
			moveTo(getCurrentPosition() + event.getUnitsToScroll(), true);
		}
	}
	
	class KeyboardListener implements KeyListener
	{
		@Override
		public void keyPressed(KeyEvent e) {
	        switch(e.getKeyCode())
	        {
	            case KeyEvent.VK_PAGE_UP:
	            	moveTo(getCurrentPosition()-getVisibleLines()+1, true);
	                break;
	            case KeyEvent.VK_PAGE_DOWN:
	            	moveTo(getCurrentPosition()+getVisibleLines()-1, true);
	                break;
	            case KeyEvent.VK_END:
	            	moveTo(Integer.MAX_VALUE, true);
	                break;
	            case KeyEvent.VK_HOME:
	            	moveTo(0, true);
	                break;
	            case KeyEvent.VK_UP:
	            	moveTo(getCurrentPosition()-1, true);
	            	break;
	            case KeyEvent.VK_DOWN:
	            	moveTo(getCurrentPosition()+1, true);
	            	break;
	        }
		}

		@Override
		public void keyReleased(KeyEvent arg0) {
		}

		@Override
		public void keyTyped(KeyEvent arg0) {
		}
	}
}
